/**
 * @fileoverview This is the file you have to include in your html code to use Gamejs.
 * GameJs will call back the function you provide to `$r.ready()` once it has
 * loaded itself & all assets you wanted preloaded.
 * For example:
 *
 *       <html>
 *       <script src="./js/lib/$require.js"></script>
 *
 *       $r.preload([
 *          "./images/ship.png",
 *          "./images/rocket.png",
 *          "./images/planet.png",   
 *       });
 *
 *       $r.ready(function() {
 *           // your game code here
 *       }
 *
 *       </script>
 *       </html>
 *
 *
 * After that you can call ready(fn) with the callback to your main code - ready() will 
 * wait for the document and all modules to load before it calls fn().
 *
 * tested with FF 3.6+, not supposed to work in IE.
 *
 */

(function (global) {
global.require = function() {};
global.module = {};
global.$r = $r = {};
var exports = {};
var MODULES = [];
var MODULE_CODE = [];
var RESOURCES = [];

$r.GAMEJS = [
   "gamejs.js",
   
   "gamejs/util.js",
   "gamejs/utils/arrays.js",
   
   "gamejs/event.js",
   "gamejs/time.js",
   "gamejs/matrix.js",
   "gamejs/transform.js",
   "gamejs/image.js",
   "gamejs/draw.js",
   "gamejs/sprite.js",
   "gamejs/display.js",
   
   "gamejs/font.js",
   
   "gamejs/mixer.js",

   "gamejs/extra.js",
   "gamejs/extra/iso.js",
];

/**
 * Pass in array of required modules. relative to require.js location!
 */
$r.require = function(modules) {
   MODULES = MODULES.concat(modules);
   return;
};

/**
 * ReadyFn is called once all modules and assets are loaded.
 */
$r.ready = exports.ready =  function(readyFn) {
   bootstrap();
   var _ready = function() {
      if (!document.body || PRELOADING) {
         return window.setTimeout(_ready, 13);
      }
      execModules();
      loadResources();
      window.setTimeout(_readyResources, 13);
   }

   var _readyResources = function() {
      if (gamejs.image._PRELOADING || gamejs.mixer._PRELOADING) {
         return window.setTimeout(_readyResources, 13);
      }
      gamejs.init();
      readyFn();
   }
   
   window.setTimeout(_ready, 13);
   return;
};

/**
 * what the fuck
 * @param {Array} list of resources paths
 */
$r.preload = exports.preload = function(resources) {
   RESOURCES = RESOURCES.concat(resources);
   return;
}

/**
 * delegate resource loading if module loaded
 */
var loadResources = function(file) {
   if (RESOURCES.length) {
         gamejs.image.preload(RESOURCES.filter(function(fname) {
            if (fname.indexOf('png') > -1 || fname.indexOf('jpg') > -1 || fname.indexOf('gif') > -1)
               return true;
            return false;
         }));
         gamejs.mixer.preload(RESOURCES.filter(function(fname) {
            if (fname.indexOf('ogg') > -1 || fname.indexOf('wav') > -1)
               return true;
            return false;
         }));
   }
   return;
};


/**
 * after everything was loaded into MODULE_CODE, execute in order
 */
var execModules = function() {
   var findForFile = function(file) {
      var foundMod = null;
      MODULE_CODE.forEach(function(modInfo) {
         if (modInfo.file === file) foundMod = modInfo;
      }, this);
      return foundMod;
   }
   /**
    * create scopes as extracted from file name
    */
   var getModuleScope = function(file) {
      var parts = file.slice(0, -3).split('/');
      scopes = parts.filter(function(scopePart) {
         return (['', '.', '..'].indexOf(scopePart) == -1);
      });
      var scope = global;
      debug ('[require]', 'creating scope: ' + scopes.join('.'));
      scopes.forEach(function(part) {
         if (scope[part] === undefined) scope[part] = {};
         scope = scope[part];
      }, this);
      return scope;
   } 
   
   MODULES.forEach(function(file) {
      var modInfo = findForFile(file);
      if (!modInfo) {
        error('execmodules danger module not found ' + file); 
        return;
      }
      var code = modInfo.code;
      var moduleScope = getModuleScope(file);
      var exports = moduleScope;
      with (moduleScope) {
         try {
            eval(code);
         } catch (e) {
            error('[require] error parsing file ', file);
            error('adding script as <script> for debugging');
            var locationUri = _getLocation();
            // add to head for debugging purposes
            global["exports"] = {};
            var head = document.getElementsByTagName("head")[0];
            var script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = locationUri + file;
            head.appendChild(script);
         }
      };
   }, this);
}

var error = debug = info = function() {
   if (window.console !== undefined) window.console.log(Array.prototype.slice.call(arguments).join(' '));
   return;
};

/**
 * for detecting our own location
 */
var SELF_FILENAME = '$require.js';

/** 
 * get location of this script by looking at its <script> tag
 */
var _getLocation = function() {
   var scripts = Array.prototype.slice.call(document.getElementsByTagName('script'));
   var mainSrc = null;
   scripts.forEach(function(script) {
      if (script.src.indexOf(SELF_FILENAME) > -1) {
         mainSrc = script.src;
      }
   }, this);
   if (mainSrc == null) {
      throw "could not detect " + SELF_FILENAME + "  did you rename it? don't do that";
   }
   var startIdx = mainSrc.indexOf(SELF_FILENAME);
   return mainSrc.substring(0, startIdx);
};

var PRELOADING = true;

/**
 * actual loading and scope creation
 */
var bootstrap = function() {
   var locationUri = _getLocation();
   var countLoaded = 0;

   var incrementLoaded = function() {
      countLoaded++;
      if (countLoaded === MODULES.length) {
         PRELOADING = false;
      }
   };

   MODULES.forEach(function(file) {
      loadScript(locationUri + file,
         function ready(script) {
            MODULE_CODE.push({
               'file': file,
               'code': script,
            });
            //info('[require] loaded ', file);
            incrementLoaded();

         },
         function error(e) {
            error('[require] error loading ', file);
            incrementLoaded();
         }
      );
   }, this);
   return;
};

/**
 * adapted from jquery.ajax
 */
var loadScript = function(url, onReady, onError) {
   var xhr = new window.XMLHttpRequest();
   xhr.open('GET', url, true);
   xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
   xhr.setRequestHeader("Accept", "text/javascript, application/javascript");

   var isHttpSuccess = function(xhr) {
      return ( xhr.status >= 200 && xhr.status < 300 ) ||
         xhr.status === 304 || xhr.status === 1223 || xhr.status === 0;
   }

   var requestDone = false;
   var onreadystatechange = xhr.onreadystatechange = function(isTimeout) {
      // The request was aborted
      if (!xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
	      requestDone = true;
	      info('[require]', 'aborted loading of ' + url);
	      if (xhr) xhr.onreadystatechange = function(){};

      // The transfer is complete and the data is available, or the request timed out
      } else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
	      requestDone = true;	      
	      xhr.onreadystatechange = function() {};
	      var status = isTimeout === "timeout" ? "timeout" :
                  !isHttpSuccess(xhr) ? "error" :
                  xhr.status === 304 || xhr.status === 0 ? "notmodified" :
				      "success";
	      if (status === "success" ) {
	         onReady(xhr.responseText);
	      } else if (status === "timeout") {
                      gamejs.error('loadScript problem loading script ' + url);
		      xhr.abort();
		      onError('timeout');
	      }
	      // Stop memory leaks
		   xhr = null;
      }
   }
   xhr.send(null);
   return xhr;
}

}(window));

$r.require($r.GAMEJS);
